{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "本节的其余示例会使用到这个示例文件，`example.py`。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting example.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile example.py\n",
    "# This comment appears first\n",
    "# and spans 2 lines.\n",
    "\n",
    "# This comment does not show up in the output of getcomments().\n",
    "\n",
    "\"\"\"Sample file to serve as the basis for inspect examples.\n",
    "\"\"\"\n",
    "\n",
    "\n",
    "def module_level_function(arg1, arg2='default', *args, **kwargs):\n",
    "    \"\"\"This function is declared in the module.\"\"\"\n",
    "    local_variable = arg1 * 2\n",
    "    return local_variable\n",
    "\n",
    "\n",
    "class A(object):\n",
    "    \"\"\"The A class.\"\"\"\n",
    "\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "\n",
    "    def get_name(self):\n",
    "        \"Returns the name of the instance.\"\n",
    "        return self.name\n",
    "\n",
    "\n",
    "instance_of_a = A('sample_instance')\n",
    "\n",
    "\n",
    "class B(A):\n",
    "    \"\"\"This is the B class.\n",
    "    It is derived from A.\n",
    "    \"\"\"\n",
    "\n",
    "    # This method is not part of A.\n",
    "    def do_something(self):\n",
    "        \"\"\"Does some work\"\"\"\n",
    "\n",
    "    def get_name(self):\n",
    "        \"Overrides version from A\"\n",
    "        return 'B(' + self.name + ')'\n",
    "        "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用 getmembers() 来发现对象的成员属性。\n",
    "\n",
    "此示例打印例子 example 模块中的成员。模块有几个私有属性，它们作为导入实现的一部分，以及一组 `__builtins__` 会出现在这个示例的输出中，\n",
    "在这里所有这些都被忽略了，因为它们实际上并不是该模块的一部分，而且它们组成的列表很长，影响到了输出。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A : <class 'example.A'>\n",
      "B : <class 'example.B'>\n",
      "instance_of_a : <example.A object at 0x1200c5c18>\n",
      "module_level_function : <function module_level_function at 0x10ed876a8>\n"
     ]
    }
   ],
   "source": [
    "import inspect\n",
    "import example\n",
    "\n",
    "for name, data in inspect.getmembers(example):\n",
    "    if name.startswith('__'):\n",
    "        continue\n",
    "    print('{} : {!r}'.format(name, data))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "getmembers() 的参数包含一个用于扫描(模块、类或实例)的对象，以及用于筛选返回的对象的可选断言函数。返回值是包含两个值的元组列表:成员的名称和成员的类型。检查模块包括几个这样的断言函数，它们叫 ismodule()、isclass() 等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A : <class 'example.A'>\n",
      "B : <class 'example.B'>\n"
     ]
    }
   ],
   "source": [
    "for name, data in inspect.getmembers(example, inspect.isclass):\n",
    "    print('{} : {!r}'.format(name, data))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用 getmembers() 来扫描类的方式与模块相同，不过它的成员类型会不同。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('__class__', <class 'type'>),\n",
      " ('__delattr__',\n",
      "  <slot wrapper '__delattr__' of 'object' objects>),\n",
      " ('__dict__',\n",
      "  mappingproxy({'__dict__': <attribute '__dict__' of 'A' objects>,\n",
      "                '__doc__': 'The A class.',\n",
      "                '__init__': <function A.__init__ at 0x1200b86a8>,\n",
      "                '__module__': 'example',\n",
      "                '__weakref__': <attribute '__weakref__' of 'A' objects>,\n",
      "                'get_name': <function A.get_name at 0x1200b8620>})),\n",
      " ('__dir__', <method '__dir__' of 'object' objects>),\n",
      " ('__doc__', 'The A class.'),\n",
      " ('__eq__', <slot wrapper '__eq__' of 'object' objects>),\n",
      " ('__format__', <method '__format__' of 'object' objects>),\n",
      " ('__ge__', <slot wrapper '__ge__' of 'object' objects>),\n",
      " ('__getattribute__',\n",
      "  <slot wrapper '__getattribute__' of 'object' objects>),\n",
      " ('__gt__', <slot wrapper '__gt__' of 'object' objects>),\n",
      " ('__hash__', <slot wrapper '__hash__' of 'object' objects>),\n",
      " ('__init__', <function A.__init__ at 0x1200b86a8>),\n",
      " ('__init_subclass__',\n",
      "  <built-in method __init_subclass__ of type object at 0x7f95d8c57118>),\n",
      " ('__le__', <slot wrapper '__le__' of 'object' objects>),\n",
      " ('__lt__', <slot wrapper '__lt__' of 'object' objects>),\n",
      " ('__module__', 'example'),\n",
      " ('__ne__', <slot wrapper '__ne__' of 'object' objects>),\n",
      " ('__new__',\n",
      "  <built-in method __new__ of type object at 0x10c95dc48>),\n",
      " ('__reduce__', <method '__reduce__' of 'object' objects>),\n",
      " ('__reduce_ex__', <method '__reduce_ex__' of 'object' objects>),\n",
      " ('__repr__', <slot wrapper '__repr__' of 'object' objects>),\n",
      " ('__setattr__',\n",
      "  <slot wrapper '__setattr__' of 'object' objects>),\n",
      " ('__sizeof__', <method '__sizeof__' of 'object' objects>),\n",
      " ('__str__', <slot wrapper '__str__' of 'object' objects>),\n",
      " ('__subclasshook__',\n",
      "  <built-in method __subclasshook__ of type object at 0x7f95d8c57118>),\n",
      " ('__weakref__', <attribute '__weakref__' of 'A' objects>),\n",
      " ('get_name', <function A.get_name at 0x1200b8620>)]\n"
     ]
    }
   ],
   "source": [
    "from pprint import pprint\n",
    "pprint(inspect.getmembers(example.A), width=65)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要找到类的方法，可以使用 isfunction() 断言"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('__init__', <function A.__init__ at 0x1200b86a8>),\n",
      " ('get_name', <function A.get_name at 0x1200b8620>)]\n"
     ]
    }
   ],
   "source": [
    "pprint(inspect.getmembers(example.A, inspect.isfunction))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "ismethod() 断言只识别实例的绑定方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('__init__', <bound method A.__init__ of <example.A object at 0x1200c5400>>),\n",
      " ('get_name', <bound method A.get_name of <example.A object at 0x1200c5400>>)]\n"
     ]
    }
   ],
   "source": [
    "pprint(inspect.getmembers(example.A('my name'), inspect.ismethod))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "B 的输出包括了 `get_name()`，以及父类中没有的方法，还有继承自 A 中实现的 `__init__()` 方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[('__init__', <function A.__init__ at 0x1200b86a8>),\n",
      " ('do_something', <function B.do_something at 0x1200b82f0>),\n",
      " ('get_name', <function B.get_name at 0x1200b8950>)]\n"
     ]
    }
   ],
   "source": [
    "pprint(inspect.getmembers(example.B, inspect.isfunction))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "可以通过 `getdoc()` 检索对象的 docstring。返回值是带有制表符的 `__doc__` 属性，并使用统一缩进，其中制表符扩展为空格。\n",
    "\n",
    "当它通过 `__doc__` 属性直接检索时，docstring 的第二行是缩进的，但是通过 `getdoc()` 获取时则第二行移动到了左边缘。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "B.__doc__:\n",
      "This is the B class.\n",
      "    It is derived from A.\n",
      "    \n",
      "\n",
      "getdoc(B):\n",
      "This is the B class.\n",
      "It is derived from A.\n"
     ]
    }
   ],
   "source": [
    "print('B.__doc__:')\n",
    "print(example.B.__doc__)\n",
    "print()\n",
    "print('getdoc(B):')\n",
    "print(inspect.getdoc(example.B))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "除了获得实际的 docstring 之外，如果包含源文件注释，还可以从源文件实现的对象中检索到它的注释。\n",
    "`getcomments()` 函数查看对象的源，并在对象实现的前面找到它的一行行注释。\n",
    "\n",
    "返回的行包括注释的前缀（#），但去掉了前缀（#）前面的空格。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "# This method is not part of A.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(inspect.getcomments(example.B.do_something))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "当一个模块被传递给 `getcomments()` 时，返回值始终是模块中的第一个注释。\n",
    "\n",
    "示例文件中的连续行被视为单个注释，但是一旦空白行出现，注释就被截止了。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "# This comment appears first\n",
      "# and spans 2 lines.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(inspect.getcomments(example))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果 `.py` 文件是可用的模块，则可以使用 `getsource()` 和 `getsourcelines()` 检索类或方法的原始源代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "class A(object):\n",
      "    \"\"\"The A class.\"\"\"\n",
      "\n",
      "    def __init__(self, name):\n",
      "        self.name = name\n",
      "\n",
      "    def get_name(self):\n",
      "        \"Returns the name of the instance.\"\n",
      "        return self.name\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(inspect.getsource(example.A))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "要检索单个方法的源代码，请将方法的引用传递给 `getsource()`。\n",
    "\n",
    "在这种情况下会保留原始代码的缩进格式。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    def get_name(self):\n",
      "        \"Returns the name of the instance.\"\n",
      "        return self.name\n",
      "\n"
     ]
    }
   ],
   "source": [
    "print(inspect.getsource(example.A.get_name))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用 `getsourcelines()` 而不是 `getsource()` 来检索源文件，会将其分割成单独字符串的行。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(['    def get_name(self):\\n',\n",
      "  '        \"Returns the name of the instance.\"\\n',\n",
      "  '        return self.name\\n'],\n",
      " 22)\n"
     ]
    }
   ],
   "source": [
    "import pprint\n",
    "pprint.pprint(inspect.getsourcelines(example.A.get_name))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`getsourcelines()` 的返回值是一个元组，其中包含一个字符串列表(来自源文件的行)，以及源代码出现在文件中的起始行号。\n",
    "\n",
    "如果源文件是不可用的，`getsource()` 和 `getsourcelines()` 引发 IOError。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "除了用于自省函数或方法的文档之外，还可以获得可调用函数的参数的完整说明(包括默认值)。`signature()` 函数返回一个实例的签名，其中包含关于函数参数的信息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "module_level_function(arg1, arg2='default', *args, **kwargs)\n",
      "\n",
      "Parameter details:\n",
      "  arg1\n",
      "  arg2='default'\n",
      "  *args\n",
      "  **kwargs\n"
     ]
    }
   ],
   "source": [
    "sig = inspect.signature(example.module_level_function)\n",
    "print('module_level_function{}'.format(sig))\n",
    "\n",
    "print('\\nParameter details:')\n",
    "for name, param in sig.parameters.items():\n",
    "    if param.kind == inspect.Parameter.POSITIONAL_ONLY:\n",
    "        print('  {} (positional-only)'.format(name))\n",
    "    elif param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD:\n",
    "        if param.default != inspect.Parameter.empty:\n",
    "            print('  {}={!r}'.format(name, param.default))\n",
    "        else:\n",
    "            print('  {}'.format(name))\n",
    "    elif param.kind == inspect.Parameter.VAR_POSITIONAL:\n",
    "        print('  *{}'.format(name))\n",
    "    elif param.kind == inspect.Parameter.KEYWORD_ONLY:\n",
    "        if param.default != inspect.Parameter.empty:\n",
    "            print('  {}={!r} (keyword-only)'.format(\n",
    "                name, param.default))\n",
    "        else:\n",
    "            print('  {} (keyword-only)'.format(name))\n",
    "    elif param.kind == inspect.Parameter.VAR_KEYWORD:\n",
    "        print('  **{}'.format(name))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数参数可通过签名的 parameters 属性获得。parameters 是一个有序的字典，它将参数名称映射到描述参数的参数实例。在本例中，函数的第一个参数 arg1 没有默认值，而 arg2 是有的。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "函数的签名可以被用作修饰符或用于其他函数，用以验证参数输入并提供不同的默认值。编写一个适当通用的、可重用的验证装饰器是一项特殊的挑战,由于函数接受参数命名和位置的组合，因为将传入的参数与其名称匹配起来可能会比较复杂，\n",
    "。`bind()` 和 `bind_partial()` 方法提供了处理映射的必要逻辑。它们返回一个 `BoundArguments`  实例，该实例填充与指定参数名称相关联的参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Arguments:\n",
      "arg1 = 'this is arg1'\n",
      "arg2 = 'this is arg2'\n",
      "args = ('this is an extra positional argument',)\n",
      "kwargs = {'extra_named_arg': 'value'}\n",
      "\n",
      "Calling:\n",
      "this is arg1this is arg1\n"
     ]
    }
   ],
   "source": [
    "sig = inspect.signature(example.module_level_function)\n",
    "\n",
    "bound = sig.bind(\n",
    "    'this is arg1',\n",
    "    'this is arg2',\n",
    "    'this is an extra positional argument',\n",
    "    extra_named_arg='value',\n",
    ")\n",
    "\n",
    "print('Arguments:')\n",
    "for name, value in bound.arguments.items():\n",
    "    print('{} = {!r}'.format(name, value))\n",
    "\n",
    "print('\\nCalling:')\n",
    "print(example.module_level_function(*bound.args, **bound.kwargs))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`BoundArguments`  实例含有属性 `args` 和 `kwargs`，可以用来调用函数，我们使用语法将 `tuple` 和 `dictionary` 扩展到堆栈中以用作参数。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果只有一些参数可用，`bind_partial()` 仍然会创建一个 `BoundArguments` 实例。但在添加剩下的参数之前，它可能不能完全使用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Without defaults:\n",
      "arg1 = 'this is arg1'\n",
      "\n",
      "With defaults:\n",
      "arg1 = 'this is arg1'\n",
      "arg2 = 'default'\n",
      "args = ()\n",
      "kwargs = {}\n"
     ]
    }
   ],
   "source": [
    "sig = inspect.signature(example.module_level_function)\n",
    "\n",
    "partial = sig.bind_partial(\n",
    "    'this is arg1',\n",
    ")\n",
    "\n",
    "print('Without defaults:')\n",
    "for name, value in partial.arguments.items():\n",
    "    print('{} = {!r}'.format(name, value))\n",
    "\n",
    "print('\\nWith defaults:')\n",
    "partial.apply_defaults()\n",
    "for name, value in partial.arguments.items():\n",
    "    print('{} = {!r}'.format(name, value))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "inspect 包括两种直接在类层次结构上使用的方法。第一个是 `getclasstree()`，它基于给定类和它们的基类创建树状的数据结构。返回的是列表中的每个元素，要么是带有类的 tuple，要么是它的基类，或者是另一个包含子类的元组的列表。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [],
   "source": [
    "class C(example.B):\n",
    "    pass\n",
    "\n",
    "\n",
    "class D(C, example.A):\n",
    "    pass\n",
    "\n",
    "\n",
    "def print_class_tree(tree, indent=-1):\n",
    "    if isinstance(tree, list):\n",
    "        for node in tree:\n",
    "            print_class_tree(node, indent + 1)\n",
    "    else:\n",
    "        print('  ' * indent, tree[0].__name__)\n",
    "    return"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "A, B, C, D:\n",
      " object\n",
      "   A\n",
      "     D\n",
      "     B\n",
      "       C\n",
      "         D\n"
     ]
    }
   ],
   "source": [
    "print('A, B, C, D:')\n",
    "print_class_tree(inspect.getclasstree(\n",
    "    [example.A, example.B, C, D])\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "如果调用 `getclasstree()` 时将 unique 设置为 ture 值，则输出会不同。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      " object\n",
      "   A\n",
      "     B\n",
      "       C\n",
      "         D\n"
     ]
    }
   ],
   "source": [
    "print_class_tree(inspect.getclasstree(\n",
    "    [example.A, example.B, C, D],\n",
    "    unique=True,\n",
    "))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "与类层次结构一起工作的另一个函数是 `getmro()`，它按顺序返回一个类的元组，当使用 \"方法解析顺序(MRO)\" 来获取可能从基类继承的属性时会扫描该元组中的类。该元组中的每个类只出现一次。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "B_First:\n",
      "  B_First\n",
      "  B\n",
      "  A\n",
      "  C\n",
      "  object\n",
      "\n",
      "C_First:\n",
      "  C_First\n",
      "  C\n",
      "  B\n",
      "  A\n",
      "  object\n"
     ]
    }
   ],
   "source": [
    "class C(object):\n",
    "    pass\n",
    "\n",
    "\n",
    "class C_First(C, example.B):\n",
    "    pass\n",
    "\n",
    "\n",
    "class B_First(example.B, C):\n",
    "    pass\n",
    "\n",
    "\n",
    "print('B_First:')\n",
    "for c in inspect.getmro(B_First):\n",
    "    print('  {}'.format(c.__name__))\n",
    "print()\n",
    "print('C_First:')\n",
    "for c in inspect.getmro(C_First):\n",
    "    print('  {}'.format(c.__name__))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "这个输出显示了 MRO 搜索的特性为 “深度优先” 。对于 B_First, A 也在 C 之前出现在搜索顺序中，因为 B 继承自于父类 A。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "line 5 of <ipython-input-53-97e428ab822e>\n",
      "locals:\n",
      "{'frame': <frame object at 0x7f95d8c5a768>,\n",
      " 'keyword': 'changed value of argument',\n",
      " 'kwonly': 'must be named',\n",
      " 'limit': 2,\n",
      " 'local_variable': '..'}\n",
      "\n",
      "line 5 of <ipython-input-53-97e428ab822e>\n",
      "locals:\n",
      "{'frame': <frame object at 0x7f95d8e712e8>,\n",
      " 'keyword': 'changed value of argument',\n",
      " 'kwonly': 'must be named',\n",
      " 'limit': 1,\n",
      " 'local_variable': '.'}\n",
      "\n",
      "line 5 of <ipython-input-53-97e428ab822e>\n",
      "locals:\n",
      "{'frame': <frame object at 0x7f95d8d59858>,\n",
      " 'keyword': 'changed value of argument',\n",
      " 'kwonly': 'must be named',\n",
      " 'limit': 0,\n",
      " 'local_variable': ''}\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "'..'"
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def recurse(limit, keyword='default', *, kwonly='must be named'):\n",
    "    local_variable = '.' * limit\n",
    "    keyword = 'changed value of argument'\n",
    "    frame = inspect.currentframe()\n",
    "    print('line {} of {}'.format(frame.f_lineno,\n",
    "                                 frame.f_code.co_filename))\n",
    "    print('locals:')\n",
    "    pprint.pprint(frame.f_locals)\n",
    "    print()\n",
    "    if limit <= 0:\n",
    "        return\n",
    "    recurse(limit - 1)\n",
    "    return local_variable\n",
    "recurse(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用 `stack()` 可以访问从当前帧到第一个调用者的所有堆栈帧。这个示例类似于前面所示示例，但是它等待直到递归的末尾来打印堆栈信息。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Overwriting inspect_stack.py\n"
     ]
    }
   ],
   "source": [
    "%%writefile inspect_stack.py\n",
    "import inspect\n",
    "import pprint\n",
    "\n",
    "\n",
    "def show_stack():\n",
    "    for level in inspect.stack():\n",
    "        print('{}[{}]\\n  -> {}'.format(\n",
    "            level.frame.f_code.co_filename,\n",
    "            level.lineno,\n",
    "            level.code_context[level.index].strip(),\n",
    "        ))\n",
    "        pprint.pprint(level.frame.f_locals)\n",
    "        print()\n",
    "\n",
    "\n",
    "def recurse(limit):\n",
    "    local_variable = '.' * limit\n",
    "    if limit <= 0:\n",
    "        show_stack()\n",
    "        return\n",
    "    recurse(limit - 1)\n",
    "    return local_variable\n",
    "\n",
    "\n",
    "if __name__ == '__main__':\n",
    "    recurse(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "inspect_stack.py[6]\r\n",
      "  -> for level in inspect.stack():\r\n",
      "{'level': FrameInfo(frame=<frame object at 0x10f3547a8>, filename='inspect_stack.py', lineno=6, function='show_stack', code_context=['    for level in inspect.stack():\\n'], index=0)}\r\n",
      "\r\n",
      "inspect_stack.py[19]\r\n",
      "  -> show_stack()\r\n",
      "{'limit': 0, 'local_variable': ''}\r\n",
      "\r\n",
      "inspect_stack.py[21]\r\n",
      "  -> recurse(limit - 1)\r\n",
      "{'limit': 1, 'local_variable': '.'}\r\n",
      "\r\n",
      "inspect_stack.py[21]\r\n",
      "  -> recurse(limit - 1)\r\n",
      "{'limit': 2, 'local_variable': '..'}\r\n",
      "\r\n",
      "inspect_stack.py[26]\r\n",
      "  -> recurse(2)\r\n",
      "{'__annotations__': {},\r\n",
      " '__builtins__': <module 'builtins' (built-in)>,\r\n",
      " '__cached__': None,\r\n",
      " '__doc__': None,\r\n",
      " '__file__': 'inspect_stack.py',\r\n",
      " '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x10f1b2470>,\r\n",
      " '__name__': '__main__',\r\n",
      " '__package__': None,\r\n",
      " '__spec__': None,\r\n",
      " 'inspect': <module 'inspect' from '/Users/max/anaconda2/envs/ipython/lib/python3.6/inspect.py'>,\r\n",
      " 'pprint': <module 'pprint' from '/Users/max/anaconda2/envs/ipython/lib/python3.6/pprint.py'>,\r\n",
      " 'recurse': <function recurse at 0x10f42b400>,\r\n",
      " 'show_stack': <function show_stack at 0x10f139e18>}\r\n",
      "\r\n"
     ]
    }
   ],
   "source": [
    "!python inspect_stack.py"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "inspect 模块还包括一个命令行接口，用于获取对象的详细信息，而不必在单独的 Python 程序中编写调用代码。输入是模块名和模块中的可选对象。默认输出是输入对象的源代码。使用 `--details` 参数会导致输出是打印的元数据而不是源文件。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Target: example\r\n",
      "Origin: /Users/max/making/python-library/languages_tools/example.py\r\n",
      "Cached: /Users/max/making/python-library/languages_tools/__pycache__/example.cpython-36.pyc\r\n",
      "Loader: <_frozen_importlib_external.SourceFileLoader object at 0x10a402978>\r\n",
      "\r\n",
      "\r\n"
     ]
    }
   ],
   "source": [
    "!python -m inspect -d example"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Target: example:A\r\n",
      "Origin: /Users/max/making/python-library/languages_tools/example.py\r\n",
      "Cached: /Users/max/making/python-library/languages_tools/__pycache__/example.cpython-36.pyc\r\n",
      "Line: 15\r\n",
      "\r\n",
      "\r\n"
     ]
    }
   ],
   "source": [
    "!python -m inspect -d example:A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 59,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "    def get_name(self):\r\n",
      "        \"Returns the name of the instance.\"\r\n",
      "        return self.name\r\n",
      "\r\n"
     ]
    }
   ],
   "source": [
    "!python -m inspect example:A.get_name"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
